2D-Animations In Pascal

Snukey

Hi folks!

After reading a lot about graphic programming here in HUGI, I decided to write something about 2D-animations because this topic wasn't dealth with too much but is very important for games as well as for graphic demos. We use the VGA mode 13h with 320*200 pixels and 256 colours. Moreover, I'll open a BMP picture.

For experiences coders this article is certainly nothing new but if you want to renew your knoledge please stay and correct me when I'm mistaken or you see a better solution.

Basics

Animations consists of many single pictures that will be displayed rapidly after another. In an animation one or more objects will be moved above a background which doesn't get destroyed. That means that when the object has passed this part of the screen, it looks like before.

At the beginning let's think about what possibilities there are to bring the screen to life.

Possibility 1: You first draw the background to the screen and then the to-be-moved object.

Advantages
- simple coding
- doesn't take much memory

Disadvantages
- flickers like hell (even on a P2 300 with AGP graphic card)
- you have to clean the screen every time
- you have to re-drawn its whole content

Possibility 2: You first draw the background to a virtual screen (later more about this), copy it to the screen and then draw the to-be-moved object.

Advantages
- less flickering
- doesn't take too much memory
- you don't have to re-draw the background
- the screen gets automatically cleared
- writing to the memory is faster than to Video RAM

Disadvantages
- still slightly flickering
- it takes a little more memory than possibility 1

Possibility 3: You create two virtual screens, draw the background to the first one, copy it to the second one, draw the object on it and copy the second virtual screen to the real screen.

Advantages
- no flickering
- you don't have to re-draw the background
- the screen gets automatically cleared
- writing to the memory is faster than to Video RAM

Disadvantages
- you need twice as much memory as in possibility 2
- it's a bit slow

It's also possible to re-draw only some parts of the screen but that would be too complex for this article.

Implementation

After the theory let's take a look at the first example. However, I only wrote down the required parts of the program not to make this article too long. :-)) The complete listing is included in the bonus archive.

 Procedure DIRECTSCREEN;
 BEGIN
 REPEAT
  DRAW THE BACKGROUND;
  DRAW THE OBJECT;
  CLEAR SCREEN;
 UNTIL KEYPRESSED;
 END;

This first procedure shows how the first possibility works. Actually, everyone should be able to code something like that.

I have to explain a little more about the second example because for the first time a virtual screen is used which shall be copied to the real screen later.

First, let's create the virtual screen:

   Program Beispiel;
   uses crt ;

 1 Type Vscreen = array [1..64000] of Byte ;
 2           Vzeiger = ^VScreen;
 3 Var  virscr : Vzeiger;
 4         vaddr : Word ;

 5 PROCEDURE SetVirtual;
 6 Begin
 7  GetMem (virscr,64000);
 8  vaddr := seg (^virscr);
 9 End;

This procedure tells Pascal to allocate 64000 Bytes (320*200=64000). In the first four lines we define the type Vscreen which contains our 64000 Bytes. Vzeiger is a pointer to this memory. The variable Virscr becomes a pointer to 64000 in the memory. Vaddr is the variable that will contain the segment of Virscr. We will need this variable to access to the allocated memory.

Every time such a virtual screen gets created it takes 64000, which won't be freed automatically, thus we have to call this procedure at the end of the program:

 Procedure CloseVirtual;
 Begin
  FreeMem (virscr,64000);
 end;

That was all.

If we want to draw the background to the virtual screen we will do it this way:

 Procedure PP (x,y,color:Integer;wo:word);
 Begin
  Mem[wo:x+y*320]:=color;
 end;

 Begin
  PP (100,100,1,vaddr);
 end.

That puts a pixel to the position (100/100) of the virtual screen in the colour blue. Well, nice, isn't it? No, it's not nice because we can't see the pixel as it is only on the virtual screen! So we copy the virtual screen to the real screen:

 Procedure Flip ;
 Begin
  Move (virscr^,[$A000:0],64000);
 End;

That copies the whole virtual screen to the real screen without deleting the virtual screen. Now we can draw our object on the screen. ($A000, of course, is the VGA segment.)

 pp (100,200,4,$A000);

That was all for the second possibility, and now let's come to the third. For the third possibility we need a second virtual screen, so let's create it.

 {$X+}
 Program Beispiel 2;
 uses crt;
 Type VScreen = array [1..64000] of byte;
           VZeiger = ^VScreen;
 Var Virscr,Virscr2 : Vzeiger;
        Vaddr,Vaddr2 : Word ;

 Procedure SetVirtual;
 Begin
  GetMem (Virscr,64000);
  vaddr:= seg (^virscr);
 End;

 Procedure SetVirtual2;
 Begin
  GetMem (Virscr2,64000);
  vaddr2:= seg (^virscr2);
 End;

Easy, huh? But also here, we have to free the allocated memory at the end of the program! Therefore we have to adapt the procedure above for the second virtual screen.

 Procedure CloseVirtual2;
 Begin
  FreeMem (Virscr2,64000);
 end;

We can leave PP as it is because we just have to enter vaddr2 instead of vaddr in the parameters. So we draw the background to Virscr2 by:

 Begin
  PP (100,100,1,vaddr2);
 end;

However, I'd like to change the flip procedure as the speed is more important here:

 Procedure Flip(source,dest:word);
 Begin
   asm
     push    ds
     mov     ax, [Dest]
     mov     es, ax
     mov     ax, [Source]
     mov     ds, ax
     xor     si, si
     xor     di, di
     mov     cx, 32000
     rep     movsw
     pop     ds
   end;
 End;

Here, source and dest are vaddr2 and vaddr.

 Flip (vaddr2,vaddr);

Now let's draw the object to the first virtual screen:

 pp (100,200,3,vaddr);

Here we now have the finished picture and just have to copy it to the screen.

 flip (vaddr,vga);

If you play many of these single pictures after eachother in a loop you get a pretty animation. In my example program, which you just have to compile, I just opened a BMP and moved it above the background instead of drawing a picture for each frame. That isn't beautiful but enough for this example.

The program mostly consists of Assembler routines because nobody is supposed to say: "The animation flickers like hell because Pascal is so lame", but as I'm not a good ASM coder also those ASM procedures aren't necessarily the best or the fastest ones. Partly you can also find simple Pascal because that's easier and not too much slower sometimes.

I hope it helped someone of you! Comments are welcome!

- Snukey